{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# select the backend\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "from qiskit.visualization import plot_gate_map\n",
    "\n",
    "# select fakeprovider as backend\n",
    "from qiskit_ibm_runtime.fake_provider import FakeGuadalupeV2\n",
    "\n",
    "backend = FakeGuadalupeV2()\n",
    "backend_name = backend.name\n",
    "plot_gate_map(backend)\n",
    "\n",
    "from qiskit_aer import AerSimulator\n",
    "from utils.core.benchmark import BenchmarkCircuit\n",
    "from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager\n",
    "from utils.io.npy_process import save_transition_matrix\n",
    "\n",
    "shots = 100000\n",
    "pm = generate_preset_pass_manager(optimization_level=1, backend=backend)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# submit benchmark experiment"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 1. generate benchmark data for single qubit"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Create a benchmark circuit for the backend, first we only focus on sigle qubit.\n",
    "qubit_indices = [i for i in range(backend.num_qubits)]\n",
    "\n",
    "for i in range(len(qubit_indices)):\n",
    "    benchmark_fake = BenchmarkCircuit(num_qubits=backend.num_qubits,  \n",
    "                                 qubit_indices=[i])\n",
    "    \n",
    "    # generate benchmark circuits\n",
    "    benckmark_circuis = benchmark_fake.generate_basis_circuits()\n",
    "    \n",
    "    # transpile benchmark circuits\n",
    "    benckmark_circuis = pm.run(benckmark_circuis)\n",
    "\n",
    "    # run benchmark circuits after transpile\n",
    "    job_fake = backend.run(benckmark_circuis, shots=shots) \n",
    "    \n",
    "    result_fake = job_fake.result()\n",
    "\n",
    "    # get counts for each circuit\n",
    "    fake_results = []\n",
    "    for id in range(len(benckmark_circuis)):\n",
    "        counts_dict = result_fake.get_counts(id)\n",
    "        fake_results.append(counts_dict)\n",
    "\n",
    "    # generate transition matrix\n",
    "    transition_matrix_fake = benchmark_fake.generate_transition_matrix(fake_results)\n",
    "\n",
    "    # save transition matrix\n",
    "    save_transition_matrix(transition_matrix_fake, \n",
    "                          basis_states=benchmark_fake.basis_states, \n",
    "                          filepath=f'./data/benchmark_matrix_{backend_name}/1-qubit/transition_matrix_q{i}.npy')   \n",
    "    \n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 2. generate benchmark data for two qubits"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Create a benchmark circuit for the backend, now we focus on two qubits.\n",
    "qubit_indices = [i for i in range(backend.num_qubits)]\n",
    "\n",
    "# we need to consider the order of qubits, so we need to consider the combination of qubits.\n",
    "import itertools\n",
    "qubit_combinations = list(itertools.combinations(qubit_indices, 2))\n",
    "\n",
    "for combination in qubit_combinations:\n",
    "    i, j = combination\n",
    "    benchmark_fake = BenchmarkCircuit(num_qubits=backend.num_qubits,  \n",
    "                                 qubit_indices=list(combination))\n",
    "    \n",
    "    # generate benchmark circuits\n",
    "    benckmark_circuis = benchmark_fake.generate_basis_circuits()\n",
    "    \n",
    "    # transpile benchmark circuits\n",
    "    benckmark_circuis = pm.run(benckmark_circuis)\n",
    "\n",
    "    # run benchmark circuits after transpile\n",
    "    job_fake = backend.run(benckmark_circuis, shots=shots) \n",
    "    \n",
    "    result_fake = job_fake.result()\n",
    "\n",
    "    # get counts for each circuit\n",
    "    fake_results = []\n",
    "    for id in range(len(benckmark_circuis)):\n",
    "        counts_dict = result_fake.get_counts(id)\n",
    "        fake_results.append(counts_dict)\n",
    "\n",
    "    # generate transition matrix\n",
    "    transition_matrix_fake = benchmark_fake.generate_transition_matrix(fake_results)\n",
    "\n",
    "    # save transition matrix\n",
    "    save_transition_matrix(transition_matrix_fake, \n",
    "                          basis_states=benchmark_fake.basis_states, \n",
    "                          filepath=f'./data/benchmark_matrix_{backend_name}/2-qubit/transition_matrix_full.npy')   "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 3. generate benchmark data for full qubits (sometimes we consider cnot gate add before the measurement)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Create a benchmark circuit for the backend, now we focus on full qubits.\n",
    "full_qubit_indices_combinations = [\n",
    "    [0, 1],\n",
    "    # [0, 1, 2, 3, 5, 8, 11],\n",
    "    # [0, 1, 2, 3, 5, 8, 11, 14, 13], \n",
    "]\n",
    "\n",
    "SAVE_PATH = f'./data/benchmark_matrix_{backend_name}/full_qubits'\n",
    "ADD_CNOT = True\n",
    "if ADD_CNOT:\n",
    "    SAVE_PATH = f'{SAVE_PATH}_cnot'\n",
    "\n",
    "for qubit_indices in full_qubit_indices_combinations:\n",
    "    if ADD_CNOT:\n",
    "        CNOT_MAPPING = {qubit_indices[0]: [qubit_indices[1]]}\n",
    "    benchmark_fake = BenchmarkCircuit(num_qubits=backend.num_qubits,  \n",
    "                                 qubit_indices=qubit_indices)\n",
    "    \n",
    "    # generate benchmark circuits\n",
    "    benckmark_circuis = benchmark_fake.generate_basis_circuits(add_cnot=ADD_CNOT, mapping=CNOT_MAPPING)\n",
    "    \n",
    "    # transpile benchmark circuits\n",
    "    benckmark_circuis = pm.run(benckmark_circuis)\n",
    "\n",
    "    # run benchmark circuits after transpile\n",
    "    job_fake = backend.run(benckmark_circuis, shots=shots) \n",
    "    \n",
    "    result_fake = job_fake.result()\n",
    "\n",
    "    # get counts for each circuit\n",
    "    fake_results = []\n",
    "    for id in range(len(benckmark_circuis)):\n",
    "        counts_dict = result_fake.get_counts(id)\n",
    "        fake_results.append(counts_dict)\n",
    "\n",
    "    # generate transition matrix\n",
    "    transition_matrix_fake = benchmark_fake.generate_transition_matrix(fake_results, cnot_mapping=CNOT_MAPPING)\n",
    "\n",
    "    # save transition matrix\n",
    "    if ADD_CNOT:\n",
    "        save_transition_matrix(transition_matrix_fake, \n",
    "                              basis_states=benchmark_fake.basis_states, \n",
    "                              filepath=f'{SAVE_PATH}/transition_matrix_q{qubit_indices}_cnot.npy')   \n",
    "    else:\n",
    "        save_transition_matrix(transition_matrix_fake, \n",
    "                              basis_states=benchmark_fake.basis_states, \n",
    "                              filepath=f'{SAVE_PATH}/transition_matrix_q{qubit_indices}.npy')   "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[9.8598e-01, 2.9520e-02, 4.4060e-02, 3.2500e-03],\n",
       "       [6.6800e-03, 9.6119e-01, 1.2400e-03, 4.1220e-02],\n",
       "       [6.3900e-03, 1.6900e-03, 9.4829e-01, 2.7760e-02],\n",
       "       [9.5000e-04, 7.6000e-03, 6.4100e-03, 9.2777e-01]])"
      ]
     },
     "execution_count": 3,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "transition_matrix_fake"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 4. generate benchmark data for qubit pair with coupling relationship"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Create benchmark circuits for the backend, now we focus on qubit pair with coupling relationship.\n",
    "coupling_pairs = backend.configuration().coupling_map\n",
    "\n",
    "for coupling_pair in coupling_pairs:\n",
    "    i, j = coupling_pair\n",
    "    benchmark_fake = BenchmarkCircuit(num_qubits=backend.num_qubits,  \n",
    "                                 qubit_indices=coupling_pair)\n",
    "    \n",
    "    # generate benchmark circuits\n",
    "    benckmark_circuis = benchmark_fake.generate_basis_circuits()\n",
    "    \n",
    "    # transpile benchmark circuits\n",
    "    benckmark_circuis = pm.run(benckmark_circuis)\n",
    "\n",
    "    # run benchmark circuits after transpile\n",
    "    job_fake = backend.run(benckmark_circuis, shots=shots) \n",
    "    \n",
    "    result_fake = job_fake.result()\n",
    "\n",
    "    # get counts for each circuit\n",
    "    fake_results = []\n",
    "    for id in range(len(benckmark_circuis)):\n",
    "        counts_dict = result_fake.get_counts(id)\n",
    "        fake_results.append(counts_dict)\n",
    "\n",
    "    # generate transition matrix\n",
    "    transition_matrix_fake = benchmark_fake.generate_transition_matrix(fake_results)\n",
    "\n",
    "    # save transition matrix\n",
    "    save_transition_matrix(transition_matrix_fake, \n",
    "                          basis_states=benchmark_fake.basis_states, \n",
    "                          filepath=f'./data/benchmark_matrix_{backend_name}_coupling/transition_matrix_q{i}_q{j}.npy')   "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 5. generate benchmark data for qubit structure(line, grid, kNN)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "find the number of the line structures of length 3: 20\n",
      "find the number of the grid structures of size 2: 0\n",
      "find the number of the k-nearest neighbors of k=3: 16\n",
      "find the number of the k-nearest neighbors of k=3 and source qubit=8: 1\n",
      "find the number of the k-nearest neighbors of k=3 and source qubit=8 with exact distance: 1\n"
     ]
    }
   ],
   "source": [
    "from utils.core.structure import find_line_structures, find_grid_structures, find_knn_qubits\n",
    "import networkx as nx\n",
    "\n",
    "length = 3\n",
    "min_size = 2\n",
    "k = 3\n",
    "\n",
    "coupling_graph = nx.Graph()\n",
    "for i, j in backend.coupling_map:\n",
    "    coupling_graph.add_edge(i, j)\n",
    "\n",
    "line_structures = find_line_structures(coupling_graph, length=length)\n",
    "grid_structures = find_grid_structures(coupling_graph, min_size=min_size)\n",
    "knn_qubits = find_knn_qubits(coupling_graph, k=k)\n",
    "# Moreover, you can specify the source qubit to find the k-nearest neighbors of the source qubit.\n",
    "# The coupling graph is recommended so that you can find the k-nearest neighbors of the source qubit.\n",
    "knn_qubits_source_8 = find_knn_qubits(coupling_graph, k=k, source_qubit=8)\n",
    "knn_qubits_source_8_exact = find_knn_qubits(coupling_graph, k=k, source_qubit=8, exact_distance=True)\n",
    "\n",
    "print(f\"find the number of the line structures of length {length}: {len(line_structures)}\")\n",
    "print(f\"find the number of the grid structures of size {min_size}: {len(grid_structures)}\")\n",
    "print(f\"find the number of the k-nearest neighbors of k={k}: {len(knn_qubits)}\")\n",
    "print(f\"find the number of the k-nearest neighbors of k={k} and source qubit=8: {len(knn_qubits_source_8)}\")\n",
    "print(f\"find the number of the k-nearest neighbors of k={k} and source qubit=8 with exact distance: {len(knn_qubits_source_8_exact)}\")\n",
    "\n",
    "# and then submit the benchmark circuits for the qubit structure.\n",
    "# # 1. line structure\n",
    "# line_structure_circuits = benchmark_fake.generate_basis_circuits(line_structures)\n",
    "\n",
    "# # 2. grid structure\n",
    "# grid_structure_circuits = benchmark_fake.generate_basis_circuits(grid_structures)\n",
    "\n",
    "# # 3. k-nearest neighbors\n",
    "# knn_circuits = benchmark_fake.generate_basis_circuits(knn_qubits)\n",
    "\n",
    "# # 4. k-nearest neighbors of the source qubit\n",
    "# knn_source_8_circuits = benchmark_fake.generate_basis_circuits(knn_qubits_source_8)\n",
    "\n",
    "# # ...\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 6.generate benchmark for constructing logical transition matrix"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "metadata": {},
   "outputs": [],
   "source": [
    "from qiskit_aer import AerSimulator\n",
    "from utils.core.benchmark import BenchmarkCircuit\n",
    "from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager\n",
    "from utils.io.npy_process import save_transition_matrix\n",
    "import json\n",
    "\n",
    "shots = 100000\n",
    "pm = generate_preset_pass_manager(optimization_level=1, backend=backend)\n",
    "\n",
    "qubit_indices = [0, 1]\n",
    "mapping = {qubit_indices[0]: [qubit_indices[1]]}\n",
    "state_mapping = {'00': '0', '11': '1'}\n",
    "add_gates = False\n",
    "\n",
    "benchmark_fake = BenchmarkCircuit(num_qubits=len(qubit_indices),  \n",
    "                                 qubit_indices=qubit_indices)\n",
    "    \n",
    "# generate benchmark circuits\n",
    "benckmark_circuis = benchmark_fake.generate_logical_basis_circuits(mapping=mapping, add_x_gates=add_gates)\n",
    "    \n",
    "# transpile benchmark circuits\n",
    "benckmark_circuis = pm.run(benckmark_circuis)\n",
    "\n",
    "# run benchmark circuits after transpile\n",
    "job_fake = backend.run(benckmark_circuis, shots=shots) \n",
    "\n",
    "result_fake = job_fake.result()\n",
    "\n",
    "# get counts for each circuit\n",
    "fake_results = []\n",
    "for id in range(len(benckmark_circuis)):\n",
    "    counts_dict = result_fake.get_counts(id)\n",
    "    fake_results.append(counts_dict)\n",
    "\n",
    "# generate transition matrix\n",
    "transition_matrix_fake = benchmark_fake.generate_transition_matrix(fake_results, mapping=state_mapping)\n",
    "\n",
    "# save transition matrix\n",
    "# Create a valid filename by replacing invalid characters in the mapping\n",
    "mapping_str = json.dumps(state_mapping).replace('\"', '').replace(':', ', ').replace(' ', '')\n",
    "save_transition_matrix(transition_matrix_fake, \n",
    "                        basis_states=benchmark_fake.logical_basis_states, \n",
    "                        filepath=f'./data/logical_benchmark_matrix_{backend_name}/transition_matrix_{mapping_str}.npy')   "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[0.99879854, 0.0022345 ],\n",
       "       [0.00120146, 0.9977655 ]])"
      ]
     },
     "execution_count": 19,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "transition_matrix_fake"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": ".venv",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.9.21"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
